//
// Copyright (C) 2010 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA).  All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3.  The NOSA has been approved by the Open Source
// Initiative.  See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//

package gov.nasa.jpf.jvm;

import gov.nasa.jpf.Config;
import gov.nasa.jpf.JPF;
import gov.nasa.jpf.JPFConfigException;
import gov.nasa.jpf.util.FileUtils;
import gov.nasa.jpf.util.JPFLogger;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;

/**
 * very simple URLConnection model that can be used for reading static URL contents
 *
 * The data can be stored in/read from the file system, and is cached
 * to avoid DOS by means of model checking
 */
public class JPF_gov_nasa_jpf_CachedROHttpConnection {

  static JPFLogger logger = JPF.getLogger("http");

  static File cacheDir;
  static HashMap<String,byte[]> dataCache;

  public static void init (Config conf){
    String cacheDirPath = conf.getString("http.cache_dir");
    if (cacheDirPath != null){
      cacheDir = new File(cacheDirPath);

      if (!cacheDir.exists()){
        cacheDir.mkdir();
      }
      if (!cacheDir.isDirectory()){
        throw new JPFConfigException("illegal http.cache_dir entry: " + cacheDirPath);
      }
    }

    dataCache = new HashMap<String,byte[]>();
  }

  private static String getCacheFileName( String url){
    String fn = url.replace('/', '^');
    fn = fn.replace(':', '%');

    return fn;
  }

  private static byte[] getDataFromCachedFile (String url){
    byte[] data = null;
    String cacheFileName = getCacheFileName(url);
    File cacheFile = new File(cacheDir, cacheFileName);
    if (cacheFile.isFile()) {
      try {
        data = FileUtils.getContents(cacheFile);
      } catch (IOException iox) {
        logger.warning("can't read http data from cached file ", cacheFile.getPath());
      }

      if (data != null) {
        logger.info("reading contents of ", url, " from file ", cacheFile.getPath());
        dataCache.put(url, data);
      }
    }

    return data;
  }

  private static byte[] getDataFromURL (String surl){
    byte[] data = null;

    try {
      URL url = new URL(surl);
      InputStream is = url.openStream();

      if (is != null) {
        ByteArrayOutputStream os = new ByteArrayOutputStream(is.available());
        byte[] buf = new byte[1024];

        for (int n = is.read(buf); n >= 0; n = is.read(buf)) {
          os.write(buf, 0, n);
        }
        is.close();

        data = os.toByteArray();
        dataCache.put(surl, data);

        logger.info("reading contents of ", surl, " from server");

        if (cacheDir != null) {
          String cacheFileName = getCacheFileName(surl);
          File cacheFile = new File(cacheDir, cacheFileName);
          try {
            FileUtils.setContents(cacheFile, data);
            logger.info("storing contents of ", surl, " to file ", cacheFile.getPath());
          } catch (IOException iox) {
            logger.warning("can't store to cache directory ", cacheFile.getPath());
          }
        }

        return data;
      }
    } catch (MalformedURLException mux){
      logger.warning("mallformed URL ", surl);
    } catch (IOException ex) {
      logger.warning("reading URL data ", surl, " failed with ", ex.getMessage());
    }

    return data;
  }

  public static int getContents__Ljava_lang_String_2___3B (MJIEnv env, int objRef, int surlRef){
    String url = env.getStringObject(surlRef);

    // first we check if it's already cached in memory
    byte[] data = dataCache.get(url);

    if (data != null){
      logger.info("using cached contents of ", url);

    } else {
      // see if we can get it from the cacheDir
      if (cacheDir != null){
        data = getDataFromCachedFile(url);
      }

      // if that didn't produce anything, we have to reach out to the net
      if (data == null){
        data = getDataFromURL( url);
      }
    }

    if (data != null){
      return env.newByteArray(data);
    } else {
      return MJIEnv.NULL;
    }
  }
}
